use graphics::{Box2D, Frame, Point, Size, Vector};

use crate::contexts::{EventCtx, LayoutCtx, LifeCycleCtx, PaintCtx};
use crate::event::{Event, LifeCycle};
use crate::widget::Pod;
use crate::widget::Widget;

pub struct Scroll {
    pub children: Pod,
    pub size: Size,
    pub offset: Vector,
}

impl Scroll {
    pub fn new(children: Pod) -> Self {
        Self {
            children,
            size: Default::default(),
            offset: Default::default(),
        }
    }

    pub fn children_mut(&mut self) -> &mut Pod {
        &mut self.children
    }
}

impl Widget for Scroll {
    fn event(&mut self, cx: &mut EventCtx, event: &Event) {
        let modified_event = match event {
            Event::MousePressed(info) => {
                let mut mouse_info = info.clone();
                if !cx.is_hot() {
                    let x = (mouse_info.pos.x - self.size.width).max(0.0);
                    let y = (mouse_info.pos.y - self.size.height).max(0.0);
                    if x != 0.0 && y != 0.0 || x == 0.0 && y == 0.0 {
                        mouse_info.pos =
                            (self.children.state.size.to_vector() + Vector::new(x, y)).to_point();
                    } else if x == 0.0 && y != 0.0 {
                        mouse_info.pos =
                            Point::new(mouse_info.pos.x, self.children.state.size.height + y);
                    } else if x != 0.0 && y == 0.0 {
                        mouse_info.pos =
                            Point::new(self.children.state.size.width + x, mouse_info.pos.y);
                    }
                }
                mouse_info.pos -= self.offset;
                Some(Event::MousePressed(mouse_info))
            }
            Event::MouseReleased(info) => {
                let mut mouse_info = info.clone();
                if !cx.is_hot() {
                    let x = (mouse_info.pos.x - self.size.width).max(0.0);
                    let y = (mouse_info.pos.y - self.size.height).max(0.0);
                    if x != 0.0 && y != 0.0 || x == 0.0 && y == 0.0 {
                        mouse_info.pos =
                            (self.children.state.size.to_vector() + Vector::new(x, y)).to_point();
                    } else if x == 0.0 && y != 0.0 {
                        mouse_info.pos =
                            Point::new(mouse_info.pos.x, self.children.state.size.height + y);
                    } else if x != 0.0 && y == 0.0 {
                        mouse_info.pos =
                            Point::new(self.children.state.size.width + x, mouse_info.pos.y);
                    }
                }
                mouse_info.pos -= self.offset;
                Some(Event::MouseReleased(mouse_info))
            }
            Event::MouseMove(info) => {
                let mut mouse_info = info.clone();
                if !cx.is_hot() {
                    let x = (mouse_info.pos.x - self.size.width).max(0.0);
                    let y = (mouse_info.pos.y - self.size.height).max(0.0);
                    if x != 0.0 && y != 0.0 || x == 0.0 && y == 0.0 {
                        mouse_info.pos =
                            (self.children.state.size.to_vector() + Vector::new(x, y)).to_point();
                    } else if x == 0.0 && y != 0.0 {
                        mouse_info.pos =
                            Point::new(mouse_info.pos.x, self.children.state.size.height + y);
                    } else if x != 0.0 && y == 0.0 {
                        mouse_info.pos =
                            Point::new(self.children.state.size.width + x, mouse_info.pos.y);
                    }
                }
                mouse_info.pos -= self.offset;
                Some(Event::MouseMove(mouse_info))
            }
            Event::MouseWheel(info) => {
                let mut mouse_info = info.clone();
                if !cx.is_hot() {
                    let x = (mouse_info.pos.x - self.size.width).max(0.0);
                    let y = (mouse_info.pos.y - self.size.height).max(0.0);
                    if x != 0.0 && y != 0.0 || x == 0.0 && y == 0.0 {
                        mouse_info.pos =
                            (self.children.state.size.to_vector() + Vector::new(x, y)).to_point();
                    } else if x == 0.0 && y != 0.0 {
                        mouse_info.pos =
                            Point::new(mouse_info.pos.x, self.children.state.size.height + y);
                    } else if x != 0.0 && y == 0.0 {
                        mouse_info.pos =
                            Point::new(self.children.state.size.width + x, mouse_info.pos.y);
                    }
                }
                mouse_info.pos -= self.offset;
                Some(Event::MouseWheel(mouse_info))
            }
            _ => None,
        };
        let event = modified_event.as_ref().unwrap_or(event);
        self.children.event(cx, &event);
        if !cx.is_handled {
            if let Event::MouseWheel(info) = event {
                self.offset = (self.offset + info.wheel_delta)
                    .to_size()
                    .clamp(self.size - self.children.state.size, Size::zero())
                    .to_vector();
                cx.set_handled(true)
            }
        }
    }

    // К моменду вызова этого метода мы уже расставили все origin для всех Pod в els
    fn paint(&mut self, cx: &mut PaintCtx, g: &mut Frame) {
        let border = Box2D::from_origin_and_size(cx.widget_state.origin, self.size);
        g.set_clip(Some(border));
        g.translate(self.offset);
        self.children.paint_raw(cx, g);
        g.set_clip(None);
        g.translate(-self.offset)
    }

    fn layout(&mut self, cx: &mut LayoutCtx) -> Size {
        let size = self
            .children
            .layout(cx)
            .clamp(Size::zero(), Size::new(300.0, 300.0));
        self.size = size;
        size
    }

    fn lifecycle(&mut self, cx: &mut LifeCycleCtx, event: &LifeCycle) {
        self.children.lifecycle(cx, event)
    }
}
